home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 19 / Mac Magazin and MacEasy Magazine CD - Issue 19.iso / Musik & Kunst / Ear Workout 2.1 / source code / get_period.cp < prev    next >
Text File  |  1996-01-09  |  16KB  |  431 lines

  1.     int
  2. get_period(
  3.     unsigned char *data,    //-- your data
  4.     int n,            //-- number of data points
  5.     int lo_p,        //-- shortest possible period
  6.     int hi_p,        //-- longest possible period
  7.     short *grid,        //-- storage you allocate for me (call me with n=0
  8.                 //    to find out how many bytes)
  9.     int grid_space,        //-- number of bytes you allocated for me
  10.     int *naverage,        //-- number of periods averaged together to get this
  11.                 //    result (output)
  12.     short **nhit_ptr,    //--pointer to the histogram; 256 bins per octave;
  13.                 //    bin number=256*log2(p/lo_p)
  14.     int *nhist,        //--tells them how many bins in histogram,
  15.     int **int_info_handle,    //--Passes back a pointer to some handy data:
  16.                 //    *int_info[0]=mean abs. val of deviation from mean
  17.                 //    *int_info[1]=mean abs. val of derivative
  18.                 //    (Means are only based on a subset of the
  19.                 //    data.)
  20.                 //    *int_info[2]=xshift
  21.                 //    *int_info[3]=yshift
  22.                 //    *int_info[4]=tstep
  23.     double **double_info_handle
  24.                 //
  25.     );
  26.  
  27. /*
  28. Function get_period returns period of the waveform in units of the 
  29.     sampling period divided by 256.
  30. Returns 0 on error.
  31. If you call it with n=0, it returns the number of bytes you should
  32. allocate for it storage.
  33.  
  34. This is intended to work for a slightly non-periodic waveform, i.e. one
  35. whose fourier spectrum consists of peaks at frequencies which are very nearly
  36. multiples of a fundamental frequency.
  37.  
  38. The concept:
  39. One could just watch the waveform y(t) pass through a certain point (say y=0)
  40. and then watch for the next time it passes throught that point.  The difference
  41. in time would be the period.  Two problems:
  42. (1) Periodic waveforms may pass through the same y value several times per period.
  43. (2) For a typical sampling frequency, one period may be only 10-100 samples, so
  44.     we'd like to average several measurements together.
  45.     
  46. Solution to (1):
  47. A truly periodic waveform would come back to the same y', y'', y''', ...,
  48. not just the same y.  We can make a grid in the "phase space" that has
  49. y on one axis and y' on the other axis, and watch for when the wavform comes
  50. back to the same cell in phase space.  As a matter of fact, a curve in a bounded
  51. 2-d region pretty much has to cross itself, so we're almost guaranteed to hit
  52. cells repeatedly.  (We could generalize this to 3-d, but the property of guaranteed
  53. crossing is lost, and also the 2nd derivative is very granular with an 8-bit
  54. adc.)
  55. Looking at phase space plots of almost periodic functions shows that
  56. a typical plot is sort of a cardiod shape, possibly with a little loop in it
  57. that is not a full period.  However, the self-crossing created by the little
  58. loop does not usually have the same 2nd derivative -- the curve typically executes
  59. that crossing at a near-right angle.  The self-crossing at one full period, however,
  60. typically happens at a shallow angle.  This means that the correct full-period
  61. crossing generates a lot of return visits to cells in phase space, while the
  62. incorrect crossing generated by the little loops generates far fewer return
  63. visits.  So a good method is to look for lots of crossings that have about the
  64. same period.
  65.  
  66. Solution to (2):
  67. Every time we get a crossing, take the log base 2 of its period (times 256).
  68. Make a histogram of this quantity.  The peaks of the histogram are the
  69. periods we've seen frequently.  For each histogram bin, also store the sum of
  70. the periods, so we can divide later by the number of hits to get an average period.
  71. Also include neighboring histogram bins in the average to avoid funny effects
  72. associated with bin boundaries.
  73.  
  74. Fine points:
  75.  
  76. How to decide on the size of the cells in phase space?
  77. Take a sample from the data and take the average absolute value.  Do a bit shift
  78. on the y and y' values so that the resulting range approximately fills half the
  79. size of the grid.  If we make our cells in phase space too small, we may not
  80. get any crossings -- if this happens, increase the bit shift by one and try again.
  81.  
  82. We may have several data points in a row in the same cell of phase space.  Only
  83. use the first one to fall into a given cell.
  84.  
  85. Actual samples have a tendency to drift across the phase space plot -- I subtract
  86. out a running average from each data point.
  87.  
  88. Current status:
  89. Tested with singing, whistling and guitar.  Works well for signals of sufficient
  90. amplitude.  For weak signals, the granularity is too poor for the phase-space
  91. concept to work, and the method tends to find a broad, strong false histogram peak
  92. at low periods and only a much weaker peak at the correct, longer period.  For
  93. most of these weak signals, though, I can still hear a note with a definite pitch
  94. when I play it back.  This points up the advantages of the frequency domain:
  95. you get a sum over many periods, which improves the signal-to-noise.
  96. Seems to work quite well with whistling, which is probably more sinusoidal,
  97. and not as well with singing.  With singing, seems to get confused by higher
  98. harmonics.  Do some integration?
  99.  
  100. Idea:
  101. Normally you want some _low-pass_ filtering before you do your pitch detection,
  102. but by using y and y' for my phase-space plots, I'm essentially doing some
  103. _high-pass_ filtering (differentiation to get y').  How about constructing the
  104. phase-space plot from y and the _integral_ of y?  Would have to blank it
  105. out at start to avoid transients.
  106.  
  107. Possible improvement: First do the phase-space method,
  108. then fft with severe resampling to make it fast enough.
  109. The phase-space method is immune to
  110. aliasing (other than that of the hardware sampling freq), but prone
  111. to the problem described above for weak signals.  The fft is more
  112. likely to get a good peak for a weak signal, but has problem with aliasing.
  113. If the fft and phase-space method agree on some fairly strong peak, that's
  114. probably it. 
  115.  
  116. Problem:  Tries to extract a period, even if the sound is just noise (e.g.
  117. the consonant "sh").
  118.  
  119. Notes from Musical Applications of Microprocessors, Hal Chamberlin, Hayden, 1980:
  120.   A big challenge: sounds with _quick decays_, because they're non-periodic.
  121.   Suggested test suite:
  122.       a) sine wave
  123.       b) sine-like wave with very flat crests
  124.       c) missing fundamental
  125.       d) strong harmonic
  126.       e) wave in which a given y and y' are repeated every half-period
  127.       f) speech
  128.       g) rapid tremolo
  129.       h) white noise (should be correctly flagged by routine)
  130.   Time-domain methods:
  131.       low-pass filtering to emphasize fundamental, but:
  132.           repeated integration emphasizes low-f transients, and
  133.               may blank out routine for a long time
  134.           don't know a priori where fundamental is
  135.           might not have fundamental
  136.       my idea: would distortion restore a missing fundamental?
  137.       typical method:
  138.           Put signal through diode to select only pos peaks.
  139.           Charge up capacitor on peak of waveform.  Attenuate 1-5%.
  140.           Define period in terms of crossing of original and
  141.           altered signal.
  142.       variation:
  143.           Do the above once with positive side of signal, once
  144.           with neg side.  If 2 estimates disagree, choose the
  145.           lower period.
  146.       Gold & Rabiner:
  147.           Not really clearly described.  Six-way majority logic,
  148.           involving peak-peak, valley-valley, peak-valley,...
  149.           Decision tree using these data combined with result
  150.           of previous two determinations.
  151.   Autocorrelation:
  152.       Can use true autocorrelation, f(x) * f(x+k), or things
  153.       like |f(x)-f(x+k)|.  Look for max or min of autocorrel w.r.t. k.
  154.       Weaknesses: can give peaks at other harmonics; pitch shifts
  155.        if waveform changes.
  156.   Cepstrum techniques:
  157.       Def of cepstrum:
  158.           Do fft to get power spectrum, take log on y axis.
  159.           Take another forward (?) fft.
  160.           This gives a cepstrum plot.  X axis has dimensions of time,
  161.           but it's called quefrency.
  162.       Idea: power spectrum has a bunch of harmonics, evenly spaced.
  163.           Fft on that gives spacing of harmonics, which is
  164.           freq. of fundamental.
  165.           Low-quefrency part of plot tells about envelope of resonances,
  166.           high-quefrency tells about the harmonics that excited
  167.           the resonances.
  168.           Reason: by taking log of (excitation fn)*(resonance curve),
  169.           you're making it into a _sum_, which fft can separate.
  170.       My misgiving:  what if power spectrum is very small at some point...
  171.           then log power spectrum has huge neg peak?????
  172.       Problem: fails if only odd harmonics are present.
  173.  */
  174.  
  175.  
  176. unsigned char log2_256_lookup(unsigned char x);
  177. int quick_log2_ratio(int x,int y);
  178.  
  179. #define XGRIDSHIFT 10
  180. #define XGRID (1<<XGRIDSHIFT)
  181. #define YGRID 128
  182. #define TOTGRID (XGRID*YGRID)
  183. #define ABSVAL(x) ((x)>=0 ? (x) : -(x))
  184. #define MAXOCTAVES 20
  185.             //...max of 20 octaves between lo_p and hi_p
  186. #define PREC 8
  187.     //...number of bits of precision of input data
  188.  
  189. #define DEBUG 0
  190.  
  191. #if DEBUG
  192. #include <stdio.h>
  193. #endif
  194.  
  195.     int
  196. get_period(
  197.     unsigned char *data,    //-- your data
  198.     int n,            //-- number of data points
  199.     int lo_p,        //-- shortest possible period
  200.     int hi_p,        //-- longest possible period
  201.     short *grid,        //-- storage you allocate for me (call me with n=0
  202.                 //    to find out how many bytes)
  203.     int grid_space,        //-- number of bytes you allocated for me
  204.     int *naverage,        //-- number of periods averaged together to get this
  205.                 //    result (output)
  206.     short **nhit_ptr,    //--pointer to the histogram; 256 bins per octave;
  207.                 //    bin number=256*log2(p/lo_p)
  208.     int *nhist,        //--tells them how many bins in histogram,
  209.     int **int_info_handle,    //--Passes back a pointer to some handy data:
  210.                 //    *int_info[0]=mean abs. val of deviation from mean
  211.                 //    *int_info[1]=mean abs. val of derivative
  212.                 //    (Means are only based on a subset of the
  213.                 //    data.)
  214.                 //    *int_info[2]=xshift
  215.                 //    *int_info[3]=yshift
  216.                 //    *int_info[4]=tstep
  217.     double **double_info_handle
  218.                 //
  219.     )
  220.     {
  221.       static short nhit[MAXOCTAVES*256];
  222.       static short sum_of_periods[MAXOCTAVES*256];
  223.       static int int_info[20];
  224.       static double double_info[20];
  225.       int i,j,x,y,u,v,xnorm,ynorm,nnorm,xshift,yshift,q,p,old,log2p,any_hits,
  226.           most_popular,log2p_below,log2p_above,nvotes,this_period,
  227.           best_period,most_votes,old_cell,running_sum,running_sum_time,
  228.           fake_running_sum_value,k,running_average,general_average,
  229.           tstep;
  230.       
  231.       if (nhit_ptr != (short **) 0) *nhit_ptr = nhit;
  232.       if (nhist != (int *) 0) *nhist = MAXOCTAVES*256;
  233.       if (int_info_handle != (int **) 0) *int_info_handle = int_info;
  234.       if (double_info_handle != (double **) 0) *double_info_handle = double_info;
  235.       
  236.       if (n==0) return TOTGRID*sizeof(short);
  237.       
  238.       if (grid_space<TOTGRID*sizeof(short)) return 0;
  239.             
  240.       //-- get a fairly random sample of
  241.       //   typical magnitude of x & y, and determine DC offset
  242.       xnorm = 0;
  243.       ynorm = 0;
  244.       nnorm = 0;
  245.       general_average = 0;
  246.       q = lo_p;
  247.       while (n/q<10) q *= 2;
  248.       for (i=1; i<n; i+=q) {
  249.         general_average += data[i];
  250.         ++nnorm;
  251.       }
  252.       general_average /= nnorm;
  253.       for (i=1; i<n; i+=q) {
  254.         u = data[i]; //--convert data to signed
  255.         v = data[i-1];
  256.         x = u-general_average;
  257.         y = u-v;
  258.         xnorm += ABSVAL(x);
  259.         ynorm += ABSVAL(y);
  260.       }
  261.       
  262.       xnorm /= nnorm;
  263.       ynorm /= nnorm;
  264.       int_info[0] = xnorm;
  265.       int_info[1] = ynorm;
  266.       
  267.       xshift = 0;
  268.       while (xnorm>>xshift > XGRID/4)
  269.         ++xshift;
  270.       
  271.       yshift = 0;
  272.       while (ynorm>>yshift > YGRID/4)
  273.         ++yshift;
  274.       if (yshift==0 && ynorm>0 && ynorm < YGRID/8)
  275.         tstep = YGRID/(8*ynorm);
  276.       else
  277.         tstep = 1;
  278.       if (tstep<1) tstep=1;
  279.       if (tstep>lo_p/4) tstep=lo_p/4;
  280.       
  281.       int_info[2] = xshift;
  282.       int_info[3] = yshift;
  283.       int_info[4] = tstep;
  284.         
  285.       fake_running_sum_value = general_average;
  286.       running_sum_time = lo_p/2;
  287.           
  288.       for (;;) {
  289.           any_hits = 0;
  290.           for (i=0; i<TOTGRID; i++) {
  291.             grid[i] = -1;
  292.           }
  293.           for (i=0; i<MAXOCTAVES*256; i++) {
  294.             nhit[i] = 0;
  295.             sum_of_periods[i] = 0;
  296.           }
  297.           running_sum = fake_running_sum_value*running_sum_time;
  298.           for (i=tstep; i<n; i++) {
  299.             running_sum += data[i];
  300.             k = i-running_sum_time;
  301.             if (k>0)
  302.               running_sum -= data[k];
  303.             else
  304.               running_sum -= fake_running_sum_value;
  305.             running_average = running_sum/running_sum_time;
  306.             u = data[i]; //--convert data to signed
  307.             v = data[i-tstep];
  308.             x = u-running_average;
  309.             y = u-v;
  310.             x = x>>xshift + XGRID/2;
  311.             if (x<0) x=0;
  312.             if (x>XGRID-1) x=XGRID-1;
  313.             y = y>>yshift + YGRID/2;
  314.             if (y<0) y=0;
  315.             if (y>YGRID-1) y=YGRID-1;
  316.             j = x + y<<XGRIDSHIFT;
  317.             old = grid[j];
  318.             grid[j] = i;
  319.             if (old>=0 && j!=old_cell && i!=1 ) {//-- deja vu
  320.               p = (i-old); //-- possible period
  321.               if (p>=lo_p && p<=hi_p) {
  322.                 log2p = quick_log2_ratio((int) p,(int) lo_p);
  323.                 if (log2p>=0 && log2p<MAXOCTAVES*256) {
  324.                   ++nhit[log2p];
  325.                   sum_of_periods[log2p] += p;
  326.                   any_hits = 1;
  327.                 }
  328.               }
  329.             }
  330.             old_cell = j;
  331.           }
  332.           if (any_hits) {
  333.             //-- find out which period is the most popular
  334.             most_votes = 0;
  335.             for (p=lo_p; p<=hi_p; p++) {
  336.               log2p_below = quick_log2_ratio((int) (p-1),(int) lo_p);
  337.               log2p_above = quick_log2_ratio((int) (p+1),(int) lo_p);
  338.               nvotes = 0;
  339.               this_period = 0;
  340.               for (log2p=log2p_below; log2p<=log2p_above; log2p++) {
  341.                 if (log2p>=0 && log2p<MAXOCTAVES*256) {
  342.                   nvotes += nhit[log2p];
  343.                   this_period += sum_of_periods[log2p];
  344.                 }
  345.               }
  346.               if (nvotes>most_votes) {
  347.                 best_period = 256*this_period;
  348.                 best_period = best_period/nvotes;
  349.                 most_votes = nvotes;
  350.               }
  351.             }
  352.             if (most_votes>0) {
  353.               *naverage = most_votes;
  354.               return best_period;
  355.             }
  356.           }
  357.           if (xshift>=PREC-1 || yshift>=PREC-1) return 0; //-- we failed;
  358.           xshift++;
  359.           yshift++;
  360.       }
  361.       
  362.       
  363.       
  364.     }
  365.  
  366.     //-- returns 256*(log base 2 of x/y)
  367.     // returns garbage (result=32000 or -32000) if x<=0 or y<=0
  368.     int
  369. quick_log2_ratio(int x,int y)
  370.     {
  371.       int z,q;
  372.       unsigned char qq;
  373.       if (y<=0) return 32000;
  374.       if (x<=0) return -32000;
  375.       z = 0;
  376.       while (x>=2*y) {
  377.         y = y*2;
  378.         z += 256;
  379.       }
  380.       while (x<y) {
  381.         x = x*2;
  382.         z -= 256;
  383.       }
  384.       q = 256*(x-y);
  385.       q = q/y;
  386.       if (q<=255)
  387.         qq = q;
  388.       else
  389.         qq = 255;
  390.       z += log2_256_lookup(qq);
  391.       return z;
  392.     }
  393.  
  394.  
  395. /*
  396.     Made the lookup table in the function log2_256_lookup() using
  397.     the following code:
  398.           {
  399.             FILE *f;
  400.             int i;
  401.             double x;
  402.             f = fopen("a.a","w");
  403.             for (i=0; i<=255; i++) {
  404.               x = i/256.;
  405.               fprintf(f,"%d",(int) (log(1.+x)/log(2.)*256.));
  406.               if (i<255) fprintf(f,",");
  407.               if ((i+1)%20==0) fprintf(f,"\n");
  408.             }
  409.             fclose(f);
  410.           }
  411. */
  412.     unsigned char
  413. log2_256_lookup(unsigned char x)
  414.     {
  415.       static unsigned char table[256] = {
  416. 0,1,2,4,5,7,8,9,11,12,14,15,16,18,19,21,22,23,25,26,
  417. 27,29,30,31,33,34,35,37,38,39,40,42,43,44,46,47,48,49,51,52,
  418. 53,54,56,57,58,59,61,62,63,64,65,67,68,69,70,71,73,74,75,76,
  419. 77,78,80,81,82,83,84,85,87,88,89,90,91,92,93,94,96,97,98,99,
  420. 100,101,102,103,104,105,106,108,109,110,111,112,113,114,115,116,117,118,119,120,
  421. 121,122,123,124,125,126,127,128,129,131,132,133,134,135,136,137,138,139,140,140,
  422. 141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,
  423. 161,162,162,163,164,165,166,167,168,169,170,171,172,173,173,174,175,176,177,178,
  424. 179,180,181,181,182,183,184,185,186,187,188,188,189,190,191,192,193,194,194,195,
  425. 196,197,198,199,200,200,201,202,203,204,205,205,206,207,208,209,209,210,211,212,
  426. 213,214,214,215,216,217,218,218,219,220,221,222,222,223,224,225,225,226,227,228,
  427. 229,229,230,231,232,232,233,234,235,235,236,237,238,239,239,240,241,242,242,243,
  428. 244,245,245,246,247,247,248,249,250,250,251,252,253,253,254,255};
  429.       return table[x];
  430.     }
  431.